package microcredit.gui.handler;

import java.awt.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import javax.swing.JOptionPane;

import microcredit.gui.editor.AbstractEditorForm;
import microcredit.gui.editor.EditorPanel;
import microcredit.gui.editor.EditorPanel.EditorDialog;
import microcredit.gui.editor.member.MemberGeneralForm;
import microcredit.gui.editor.member.MemberGroupHistoryForm;
import microcredit.gui.editor.member.MemberLoanForm;
import microcredit.gui.editor.member.MemberStatusHistoryForm;
import microcredit.gui.editor.member.MemberTrainingsForm;
import microcredit.gui.editor.training.MemberToTrainingTypeForm;
import microcredit.gui.selector.IFilter;
import microcredit.gui.selector.SelectorPanel;
import microcredit.gui.selector.SelectorPanel.SelectorDialog;
import microcredit.gui.table.BeanTable.SelectionMode;
import microcredit.gui.util.UIUtil;
import microcredit.model.Member;
import microcredit.model.MemberGroup;
import microcredit.model.MemberStatusHistory;
import microcredit.model.MemberToTrainingType;
import microcredit.model.TrainingType;
import microcredit.model.bo.BOMember;
import microcredit.model.bo.BOMemberGroup;
import microcredit.model.bo.BOTraining;
import microcredit.model.domain.Status;
import microcredit.model.domain.TrainingStatus;
import microcredit.model.util.ModelUtils;

public class MemberHandler extends AbstractHandler<Member> {

	protected final Component parent;
	
	public MemberHandler (Component parent) {
		this.parent = parent;
	}
	
	@Override
	public List<IFilter<Member>> getFilters() {
		List<IFilter<Member>> filters = new ArrayList<IFilter<Member>>();
		filters.add(new MemberNumberFilter());
		filters.add(new MemberNameFilter());
		filters.add(new MemberGroupFilter());
		filters.add(new MemberStatusFilter());
		return filters;
	}
	
	@Override
	public List<Member> load() throws Throwable {
		return BOMember.create().readMembers();
	}
	
	@Override
	public void delete(Member e) throws Throwable {
		BOMember.create().deleteMember(e);
	}
	
	@Override
	public String toString(Member element) {
		return element != null ? element.getFullName() : "";
	}
	
	@Override
	public Member save(Member entity) throws Throwable {
		MemberStatusHistory history = entity.getLastStatusHistory();
		if (history == null || !history.getStatus().equals(entity.getStatus())) {
//			MemberStatusHistoryPanel panel = new MemberStatusHistoryPanel();
//			JDialog dialog = panel.createModalDialog(parent);
//			dialog.setVisible(true);
//			history = panel.getHistory();
//			if (history == null) {
//				
//			}
		}
		return BOMember.create().saveMember(entity);
	}

	protected Member createNewEntity() {
		Member member = new Member();
		member.setHivStatus(null);
		return member;
	}
	
	@Override
	public Member newEntity(Component parent) throws Throwable {
		List<AbstractEditorForm<Member>> forms = new ArrayList<AbstractEditorForm<Member>>();
		forms.add(new MemberGeneralForm());
		forms.add(new MemberLoanForm());
		forms.add(new MemberGroupHistoryForm());
		forms.add(new MemberTrainingsForm());
		forms.add(new MemberStatusHistoryForm());
		Member e = createNewEntity();
		e = EditorPanel.showEditorDialog(parent, "New", 
				Member.class, e, this, this, null, forms);
		return e;
	}
	
	
	@Override
	public Member refresh(Member element) throws Throwable {
		// no refresh possible
		if (element.getId() == null) {
			return element;
		}
		return BOMember.create().getMember(element.getId());
	}
	
	@Override
	public Member open(Component component, Member e) throws Throwable {
		List<AbstractEditorForm<Member>> forms = new ArrayList<AbstractEditorForm<Member>>();
		forms.add(new MemberGeneralForm());
		forms.add(new MemberLoanForm());
		forms.add(new MemberGroupHistoryForm());
		forms.add(new MemberTrainingsForm());
		forms.add(new MemberStatusHistoryForm());
		
		e = EditorPanel.showEditorDialog(component, "Edit", 
				Member.class, e, this, this, null, forms);
		return e;
	}
	
	
	public static final class MemberGroupFilter implements IFilter<Member> {
		
		private ComboWrapper selectedValue;
		
		@Override
		public String getLabel() {
			return "Group";
		}
		
		public void setSelectedValue(MemberGroup selectedValue) {
			this.selectedValue = new ComboWrapper(selectedValue);
		}
		
		@Override
		public Object getSelectedValue() {
			return selectedValue;
		}
		
		@Override
		public List<? extends Object> getValues() {
			List<MemberGroup> groups = BOMemberGroup.create().readMemberGroups();
			List<ComboWrapper> wrappers = new ArrayList<ComboWrapper>();
			wrappers.add(new ComboWrapper(null));
			for (MemberGroup memberGroup : groups) {
				wrappers.add(new ComboWrapper(memberGroup));
			}
			return wrappers;
		}

		@Override
		public void filter(Object oexp, List<Member> entities) {
			ComboWrapper wrapper = (ComboWrapper)oexp;
			Long groupID = null;
			if (wrapper.group != null) {
				groupID = wrapper.group.getId();
			}
			for (Iterator<Member> iterator = entities.iterator(); iterator.hasNext();) {
				Member member = iterator.next();
				Long compareGroupID = null;
				if (member.getGroup() != null) {
					compareGroupID = member.getGroup().getId();
				}
				
				
				if (!ModelUtils.equals(groupID, compareGroupID)) {
					iterator.remove();
				}
			}
		}
		
		private class ComboWrapper {
			private final MemberGroup group;
			public ComboWrapper(MemberGroup group) {
				this.group = group;
			}
			@Override
			public String toString() {
				if (group == null) {
					return "-- no group --";
				}
				return group.getFullName();
			}
			public int hashCode() {
				if (group != null) {
					return group.hashCode();
				}
				return super.hashCode();
			}
			public boolean equals(Object obj) {
				if (group != null) {
					return group.equals(obj);
				}
				return super.equals(obj);
			}
			
		}
		
	}
	
	public static final class MemberStatusFilter implements IFilter<Member> {
		@Override
		public String getLabel() {
			return "Status";
		}

		@Override
		public List<? extends Object> getValues() {
			return Arrays.asList(Status.values());
		}

		@Override
		public Object getSelectedValue() {
			return null;
		}
		
		@Override
		public void filter(Object oexp, List<Member> entities) {
			Status exp = (Status)oexp;
				
			for (Iterator<Member> iterator = entities.iterator(); iterator
					.hasNext();) {
				Member member = iterator.next();
				if (member.getStatus() != exp) {
					iterator.remove();
				}
			}
		}
	}
	
	public static final class MemberNumberFilter implements IFilter<Member> {
		@Override
		public String getLabel() {
			return "No";
		}

		@Override
		public List<Object> getValues() {
			return null;
		}
		@Override
		public Object getSelectedValue() {
			return null;
		}

		@Override
		public void filter(Object oexp, List<Member> entities) {
			try {
				String exp = (String)oexp;
				int intValue = Integer.parseInt(exp);
				
				for (Iterator<Member> iterator = entities.iterator(); iterator
						.hasNext();) {
					Member member = iterator.next();
					if (member.getId() != intValue) {
						iterator.remove();
					}
				}
			} catch (NumberFormatException e) {
				entities.removeAll(entities);
			}
		}
	}

	public static final class MemberNameFilter implements IFilter<Member> {
		@Override
		public String getLabel() {
			return "Name";
		}

		@Override
		public List<Object> getValues() {
			return null;
		}
		
		@Override
		public Object getSelectedValue() {
			return null;
		}

		@Override
		public void filter(Object oexp, List<Member> entities) {
			String exp = ((String)oexp).toLowerCase();
			String[] expressions = exp.split("\\ ");
			for (Iterator<Member> iterator = entities.iterator(); iterator.hasNext();) {
				Member member = iterator.next();
				String fullName = member.getFullName().toLowerCase();
				for (String subExp : expressions) {
					if (fullName.indexOf(subExp) == -1) {
						iterator.remove();
						break;
					}
				} 
			}
		}
	}

	public static class ByTrainingTypeMemberHandler extends MemberHandler {

		private final TrainingType type;
		private boolean excludeMembers = false;
		
		public ByTrainingTypeMemberHandler(Component parent, TrainingType type) {
			super(parent);
			this.type = type;
		}
		
		@Override
		public void choose() throws Throwable {
			ByTrainingTypeMemberHandler handler = new ByTrainingTypeMemberHandler(parent, type);
			handler.excludeMembers = true; 
			
			SelectorDialog<Member> dialog = SelectorPanel.createDialog(parent, "Select members to assign to training type", Member.class, handler, null, null, null, handler, null, handler, null);
			dialog.getSelectorPanel().setSelectionMode(SelectionMode.MULTI);
			dialog.setVisible(true);
			
			List<Member> members = dialog.getSelectedElements();
			
			//TODO check for already assigned members
			
			if (members != null && members.size() > 0) {
				MemberToTrainingType type = new MemberToTrainingType();
				type.setTrainingType(this.type);
				type.setDate(ModelUtils.createTimestamp());
				type.setStatus(TrainingStatus.A);
				
				MemberToTrainingDataHandler trainingHandler = new MemberToTrainingDataHandler();
				List<AbstractEditorForm<MemberToTrainingType>> forms = new ArrayList<AbstractEditorForm<MemberToTrainingType>>();
				forms.add( new MemberToTrainingTypeForm());
				
				EditorDialog<MemberToTrainingType> editorDialog =  EditorPanel.createEditorDialog(parent, "Enter", MemberToTrainingType.class, type, trainingHandler, trainingHandler, null, forms);
				editorDialog.getPanel().setCloseOnSave(true);
				editorDialog.getPanel().setHideSaveMessage(true);
				editorDialog.getPanel().getCloseAction().setName("Cancel");
				editorDialog.getPanel().setAlwaysDirty(true);
				editorDialog.setVisible(true);
				type = editorDialog.getElement();
				
				BOTraining.create().assignMembersToTrainingType(members, type);
			}
		}
		
		@Override
		public List<Member> load() throws Throwable {
			if (excludeMembers) {
				return BOMember.create().readMembersNotByTrainingType(type.getId());
			} else {
				return BOMember.create().readMembersByTrainingType(type.getId());
			}
		}
	}
	
	public static class ByGroupMemberHandler extends MemberHandler {
		
		private MemberGroup memberGroup;
		private boolean excludeMembers;
		private boolean onlyActive;
		
		public ByGroupMemberHandler(Component parent, MemberGroup memberGroup, boolean onlyActive) {
			super(parent);
			this.onlyActive = onlyActive;
			this.memberGroup = memberGroup;
		}
		
		@Override
		public List<IFilter<Member>> getFilters() {
			if (excludeMembers) {
				return Arrays.asList(new MemberNumberFilter(), new MemberNameFilter(), new MemberGroupFilter(), new MemberStatusFilter());
			} else {
				return Arrays.asList(new MemberNumberFilter(), new MemberNameFilter(), new MemberStatusFilter());
			}
		}
		
		public void setMemberGroup(MemberGroup memberGroup) {
			this.memberGroup = memberGroup;
		}
		
		@Override
		protected Member createNewEntity() {
			Member m = super.createNewEntity();
			m.setGroup(memberGroup);
			return m;
		}
		
		@Override
		public void choose() throws Throwable {
			ByGroupMemberHandler handler = new ByGroupMemberHandler(parent, memberGroup, true);
			handler.excludeMembers = true;
			
			SelectorDialog<Member> dialog = SelectorPanel.createDialog(parent, "Select members to assign to group", Member.class, handler, null, null, null, handler, null, handler, null);
			dialog.getSelectorPanel().setSelectionMode(SelectionMode.MULTI);
			dialog.setVisible(true);
			
			List<Member> members = dialog.getSelectedElements();
			if (members == null || members.size() <= 0) {
				return;
			}
			List<String> membersStrings = new ArrayList<String>();
			for (Member member : members) {
				if (member.getGroup() != null) {
					membersStrings.add(member.getFullName()+" : "+member.getGroup().getFullName());
				}
			}
			if (membersStrings.size() > 0) {
				Object listMessage = UIUtil.createListMessage("The following member(s) are already assigned to groups:", membersStrings, "Do you really want to assign them to "+memberGroup.getFullName()+"?");
				int option = JOptionPane.showConfirmDialog(parent, listMessage);
				if (option != JOptionPane.YES_OPTION) {
					return;
				}
			} 
			if (members != null && members.size() > 0) {
				BOMemberGroup.create().assignMembersToGroup(members, memberGroup);
			}
		}
		
		@Override
		public List<Member> load() throws Throwable {
			return BOMember.create().readMembersByGroup(memberGroup.getId(),excludeMembers,  onlyActive);
		}
	}
	
}
